
#include <BFont>
#include <BPainter>

#include <BMouseEvent>
#include <BChangeEvent>

#include <BListWidget>
#include "member_BListItem.h"
#include "member_BListWidget.h"

using namespace BWE;

#define member					(*(member_BListWidget*)_ptr)
#define member_allocate()		_ptr = new member_BListWidget(this)
#define member_release()		delete (member_BListWidget*)_ptr

BListWidget::BListWidget(BWidget* parent) : BScrollArea(parent)
{
	member_allocate();
}
BListWidget::~BListWidget()
{
	member_release();
}

bool BListWidget::addItem(BListItem* item)
{
	if (item && !member.items.contain(item))
	{
		member.items.append(item);
		item_member(item)->setHost(this);
		member.dirtyArea = true;
		this->fresh();
		return true;
	}
	return false;
}
BListItem* BListWidget::addItem(const BString& text)
{
	BListItem* item = new BListItem(text);
	this->addItem(item);
	return item;
}
BListItem* BListWidget::addItem(BImage* image, const BString& text)
{
	BListItem* item = new BListItem(image, text);
	this->addItem(item);
	return item;
}

bool BListWidget::insertItem(int pos, BListItem* item)
{
	if (item && !member.items.contain(item) && member.items.check(pos))
	{
		member.items.insert(pos, item);
		item_member(item)->setHost(this);
		member.dirtyArea = true;
		this->fresh();
		return true;
	}
	return false;
}
BListItem* BListWidget::insertItem(int pos, const BString& text)
{
	BListItemHolder item = new BListItem(text);
	if (insertItem(pos, item))
		return item;
	return 0;
}
BListItem* BListWidget::insertItem(int pos, BImage* image, const BString& text)
{
	BListItemHolder item = new BListItem(image, text);
	if (insertItem(pos, item))
		return item;
	return 0;
}

bool BListWidget::removeItem(BListItem* item)
{
	if (member.items.contain(item))
	{
		item_member(item)->setHost(0);
		member.items.remove(item);
		member.dirtyArea = true;
		this->fresh();
		return true;
	}
	return false;
}
bool BListWidget::removeItem(const BString& text)
{
	for(int i = 0; i < member.items.size(); i++)
	{
		BListItem* item = member.items[i];
		if(item->text() == text)
		{
			item_member(item)->setHost(0);
			member.items.remove(i);
			this->fresh();
			return true;
		}
	}
	return false;
}
bool BListWidget::removeItem(int index)
{
	if(member.items.check(index))
	{
		BListItem* item = member.items[index];
		item_member(item)->setHost(0);
		member.items.remove(index);
		this->fresh();
		return true;
	}
	return false;
}
void BListWidget::clearItems()
{
	clearSelected();
	for (BListItem* item : member.items)
	{
		item_member(item)->setHost(0);
	}
	member.items.clear();
	member.hoveredItem = 0;
	member.showItemBegin = 0;
	this->fresh();
}

bool BListWidget::setSelected(BListItem* item, bool selected)
{
	if (!member.selectedItems.contain(item))
	{
		member.selectedItems.append(item);
		item_member(item)->selected = true;
		emit(Signal_ItemSelected, item);
		return true;
	}
	if (!selected && member.selectedItems.contain(item))
	{
		member.selectedItems.remove(item);
		item_member(item)->selected = false;
		emit(Signal_ItemUnSelected, item);
		return true;
	}
	return false;
}
bool BListWidget::setSelected(const BString& text, bool selected)
{
	BListItem* item = this->item(text);
	return setSelected(item, selected);
}
bool BListWidget::setSelected(int index, bool selected)
{
	BListItem* item = this->item(index);
	return setSelected(item, selected);
}
bool BListWidget::clearSelected()
{
	while (member.selectedItems.size())
	{
		auto item = member.selectedItems[0];
		item_member(item)->selected = false;
		member.selectedItems.remove(0);
		emit(Signal_ItemUnSelected, item);
	}
	return true;
}

int BListWidget::selectedCount() const
{
	return member.selectedItems.size();
}
BListItem* BListWidget::selectedItem(int index)
{
	return member.selectedItems(index);
}
const BListItem* BListWidget::selectedItem(int index) const
{
	return member.selectedItems(index);
}

int BListWidget::itemCount() const
{
	return member.items.size();
}
BListItem* BListWidget::item(int index)
{
	return member.items(index);
}
const BListItem* BListWidget::item(int index) const
{
	return member.items(index);
}

BListItem* BListWidget::item(const BString& text)
{
	for (auto item : member.items)
	{
		if (item->text() == text)
			return item;
	}
	return 0;
}
const BListItem* BListWidget::item(const BString& text) const
{
	for (auto item : member.items)
	{
		if (item->text() == text)
			return item;
	}
	return 0;
}

BListItem* BListWidget::item(const BPoint& point)
{
	for(int i = 0; i < member.showItemCount; i++)
	{
		int index = member.showItemBegin + i;
		if(index >= member.items.size())
			break;
		BListItem* item = member.items[index].ptr();
		if(item_member(item)->rect.contain(point))
			return item;
	}
	return 0;
}
const BListItem* BListWidget::item(const BPoint& point) const
{
	for(int i = 0; i < member.showItemCount; i++)
	{
		int index = member.showItemBegin + i;
		if(index >= member.items.size())
			break;
		const BListItem* item = member.items[index].ptr();
		if(item_member(item)->rect.contain(point))
			return item;
	}
	return 0;
}

BListItem* BListWidget::item(const BValue& value)
{
	for (int i = 0; i < member.items.size(); i++)
	{
		BListItem* item = member.items[i].ptr();
		if (item->value() == value)
		{
			return item;
		}
	}
	return 0;
}
const BListItem* BListWidget::item(const BValue& value) const
{
	for (int i = 0; i < member.items.size(); i++)
	{
		const BListItem* item = member.items[i].ptr();
		if (item->value() == value)
		{
			return item;
		}
	}
	return 0;
}

BSize BListWidget::sizeHint() const
{
	BSize hsize = BWidget::sizeHint();
	int rowHeight = 20;
	const BFont* font = this->font();
	if (font)
	{
		rowHeight = font->height();
	}
	hsize += BSize(100, rowHeight * 2);
	return hsize;
}

void BListWidget::scrollArea()
{
	this->fresh();
}

void BListWidget::styleEvent(const BEvent& event)
{
	const BFont* font = this->font();
	if (font)
	{
		BScrollBar* scrollBar = this->verticalScrollBar();
		if (scrollBar)
			scrollBar->setStep(font->height());
	}
	for (int i = 0; i < member.items.size(); i++)
	{
		BListItem* item = member.items[i];
		item_member(item)->dirty = true;
	}
	member.dirtyArea = true;
	this->fresh();
}
void BListWidget::resizeEvent(const BEvent& event)
{
	BScrollArea::resizeEvent(event);
	const BRect& crect = this->clientRect();
	int areaWidth = bMax(member.minAreaSize.width(), crect.width());
	int areaHeight = bMax(member.minAreaSize.height(), crect.height());
	BRect area = this->area();
	area.setSize(areaWidth, area.height());
	this->setArea(area);
}

void BListWidget::freshEvent(const BEvent& event)
{
	BScrollArea::freshEvent(event);
	if (member.dirtyArea)
	{
		member.freshArea();
		member.dirtyArea = false;
	}
	member.freshItemRects();
}
void BListWidget::paintEvent(const BEvent& event)
{
	BPainter painter(this);

	for(int i = 0; i < member.showItemCount; i++)
	{
		int index = member.showItemBegin + i;
		if(index >= member.items.size())
			break;

		const BListItem* item = member.items[index].ptr();
		if (item->isSelected())
		{
			painter.setColor(Color_Item_Selected);
			painter.fillRect(item->rect());
		}
		else if(member.hoveredItem == item)
		{
			painter.setColor(Color_Item_Hovered);
			painter.fillRect(item->rect());
		}

		if(item_member(item)->image)
		{
			BRect imageRect = item_member(item)->imageRect;
			painter.drawImage(imageRect, item_member(item)->image);
		}

		const BFont* font = painter.font();
		if(font)
		{
			BRect textRect = item_member(item)->textRect;
			painter.setColor(BColor(25, 30, 30));
			painter.drawText(textRect, item->text());
		}
	}
	painter.drawBorder();
	painter.drawPerch();
	painter.drawMargin();

	painter.setColor(255, 0, 0);
	painter.drawRect(this->area());
}

void BListWidget::mouseEnterEvent(const BMouseEvent& mouseEvent)
{
	member.hoveredItem	= 0;
}
void BListWidget::mouseLeaveEvent(const BMouseEvent& mouseEvent)
{
	if (member.hoveredItem != 0)
	{
		member.hoveredItem = 0;
		fresh();
	}
}

void BListWidget::mousePressEvent(const BMouseEvent& mouseEvent)
{
	const BPoint& pos = mouseEvent.pos();
	BListItem* pressedItem = this->item(pos);
	if (member.pressedItem != pressedItem)
	{
		member.pressedItem = pressedItem;
		this->fresh();
	}
}
void BListWidget::mouseReleaseEvent(const BMouseEvent& mouseEvent)
{
	if(member.pressedItem == 0)
	{
		member.pressedItem = 0;
		this->fresh();
	}
}
void BListWidget::mouseMoveEvent(const BMouseEvent& mouseEvent)
{
	const BPoint& pos = mouseEvent.pos();
	BListItem* hoveredItem = this->item(pos);
	if(member.hoveredItem != hoveredItem)
	{
		member.hoveredItem = hoveredItem;
		emit(Signal_ItemHovered, hoveredItem);
		this->fresh();
	}
}

void BListWidget::mouseClickEvent(const BMouseEvent& mouseEvent)
{
	if (mouseEvent.button() == Button_Left)
	{
		const BPoint& pos = mouseEvent.pos();
		BListItem* clickedItem = this->item(pos);
		if (clickedItem)
		{
			member.select(clickedItem);
			emit(Signal_ItemClicked, clickedItem);
			this->fresh();
		}
		return;
	}
	if (mouseEvent.button() == Button_Right)
	{
		const BPoint& pos = mouseEvent.pos();
		BListItem* clickedItem = this->item(pos);
		if (clickedItem)
		{
			member.select(clickedItem);
			emit(Signal_ItemRightClicked, clickedItem);
			this->fresh();
		}
	}
}
void BListWidget::mouseDoubleClickEvent(const BMouseEvent& mouseEvent)
{
	if (mouseEvent.button() != Button_Left)
		return;
	const BPoint& pos = mouseEvent.pos();
	BListItem* item = this->item(pos);
	if(item)
	{
		member.select(item);
		emit(Signal_ItemDoubleClicked, item);
		this->fresh();
	}
}

